package ar.com.framenet.base.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class EntityUtils {

	/*
	 * public static Collection<Map<String, Object>> toMap(Collection<?> c,
	 * String entityName) { Collection<Map<String, Object>> result = new
	 * ArrayList<Map<String, Object>>();
	 * 
	 * if (entityName.equals(Producto.class.getName())) { for (Object o : c) {
	 * Producto producto = (Producto) o;
	 * 
	 * Map<String, Object> map = new HashMap<String, Object>(); map.put("id",
	 * producto.getId()); map.put("nombre", producto.getNombre());
	 * map.put("descripcion", producto.getDescripcion()); map.put("precio",
	 * producto.getPrecio()); if (producto.getRubro() != null) {
	 * map.put("codRubro", producto.getRubro().getCodRubro()); }
	 * result.add(map); } } else { result = toMap(c); }
	 * 
	 * return result; }
	 */

	public static Collection<Map<String, Object>> toMap(Collection<?> c, String... excludedProperties) {
		Set<String> expSet = new HashSet<String>();
		for (String s : excludedProperties) {
			expSet.add(s);
		}
		Collection<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		for (Object o : c) {
			result.add(toMap(o, expSet));
		}
		return result;
	}

	public static Map<String, Object> toMap(Object o, String... excludedProperties) {
		Set<String> set = new HashSet<String>();
		for (String s : excludedProperties) {
			set.add(s);
		}
		return toMap(o, set);
	}

	@SuppressWarnings("unchecked")
	public static Map<String, Object> toMap(Object o, Set<String> excludedProperties) {
		Map<String, Object> map = new HashMap<String, Object>();
		Class<?> clazz = o.getClass();

		// invoque toMap method if exists and return
		try {
			Method toMapMethod = clazz.getDeclaredMethod("toMap");
			map = (Map<String, Object>) toMapMethod.invoke(o);
			return map;
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			System.out.println("entityUtils.toMap... not exists, it using reflection instead. Exception: " + e);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}

		// reflection to extract values from object
		Method[] methods = clazz.getDeclaredMethods();
		for (Method m : methods) {
			String name = m.getName();
			boolean startsWithGet = name.startsWith("get");
			boolean startsWithIs = name.startsWith("is");
			if ((startsWithGet || startsWithIs) && m.getTypeParameters().length == 0) {
				String key = name.replaceFirst(startsWithGet ? "get" : "is", "");
				key = key.substring(0, 1).toLowerCase() + key.substring(1);
				if (excludedProperties.contains(key)) {
					continue;
				}
				Object value = null;
				try {
					value = m.invoke(o);
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
				map.put(key, value);
			}
		}
		return map;
	}

	public static Object fromMap(String entityName, Map<String, String> jsonMap) {
		Class<?> clazz = null;
		Method[] methods = null;
		Object entity = null;
		try {
			clazz = Class.forName(entityName);
			entity = clazz.newInstance();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}

		Method fromMapMethod;
		try {
			fromMapMethod = clazz.getDeclaredMethod("fromMap", Map.class);
			fromMapMethod.invoke(entity, new Object[] { jsonMap });
			return entity;
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			System.out.println("entityUtils.fromMap... not exists, it using reflection instead. Exception: " + e);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}

		methods = clazz.getDeclaredMethods();
		for (Method m : methods) {
			String name = m.getName();
			boolean startsWithSet = name.startsWith("set");
			if ((startsWithSet) && m.getTypeParameters().length == 0) {
				String key = name.replaceFirst("set", "");
				key = key.substring(0, 1).toLowerCase() + key.substring(1);

				Object value = getValue(jsonMap.get(key), m.getParameterTypes()[0]);
				if (value != null)
					try {
						m.invoke(entity, new Object[] { value });
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
			}
		}

		return entity;
	}

	public static Object getValue(String valueStr, Class<?> clazz) {
		Object value = null;

		if (valueStr == null || valueStr.equals(""))
			return value;

		if (clazz.getSimpleName().equals("Integer")) {
			value = Integer.valueOf(valueStr);
		} else if (clazz.getSimpleName().equals("Float")) {
			value = Float.valueOf(valueStr);
		} else if (clazz.getSimpleName().equals("Double")) {
			value = Double.valueOf(valueStr);
		} else if (clazz.getSimpleName().equals("integer")) {
			value = Integer.parseInt(valueStr);
		} else if (clazz.getSimpleName().equals("float")) {
			value = Float.parseFloat(valueStr);
		} else {
			value = valueStr;
		}

		return value;
	}

}
